

/***
 * @Date            : 2025-01-01 16:15:58
 * @Author          : AnimateX(Baron)
 * @LastEditors     : AnimateX(Baron)
 * @LastEditTime    : 2025-01-01 16:15:59
 * @FilePath        : /include/preprocess.h
 * @CopyRight       : Copyright (c) 2025 AnimateX. All rights reserved.
 * @Description     :
 *              [01] timeStamp parser
 *              [02] decode mjpeg
 *              [04] flip, rotate, mirror, crop ....
 *              [05] undistort
 *              [06] u16 to u8 ir
 */
#pragma once
#include "common_log.h"
#include <cstdint>
#include <cstddef>
#include "jpeglib.h"
#include "jerror.h"


namespace rc {
    #pragma pack(push, 1)
    struct rgbFrameTimeStamp {
        int16_t         timestampS;
        uint64_t        timestamp;
        int32_t         adc;
        uint32_t        frameId;
    };
    #pragma pack(pop)

    #pragma pack(push, 1)
    struct irFrameTimamp {
        unsigned short  machTime;
    };
    #pragma pack(pop)

    enum class ParseError {
        SUCCESS,
        NULL_POINTER,
        BUFFER_TOO_SMALL,
        INVALID_SIGNATURE
    };

    static inline ParseError validateBuffer(const uint8_t* buf, size_t bufSize, size_t minSize) {
        if (buf == nullptr) {
            return ParseError::NULL_POINTER;
        }
        if (bufSize < minSize) {
            return ParseError::BUFFER_TOO_SMALL;
        }
        return ParseError::SUCCESS;
    }


    static inline bool parserRgbFrameTimeStamp(const uint8_t* buf, rgbFrameTimeStamp* rgbTimeStamp, int bufSize, int offset = 20) {
        // Initial logger.
        RCLogger& logger = RCLogger::getInstance();

        if (buf == nullptr || rgbTimeStamp == nullptr || bufSize <= 0) {
            logger.rs_error(" [Error] (parserRgbFrameTimeStamp) Please check input ptr! nullptr");

            return false;
        }

        // check
        const size_t requiredSize = offset + 2 + 2 + sizeof(rgbFrameTimeStamp);
        auto result = validateBuffer(buf, bufSize, requiredSize);
        if (result != ParseError::SUCCESS) {
            logger.rs_error(" [Error] Buffer validation failed: %d", static_cast<int>(result));
            return false;
        }

        // [01] offset to 0xFFE2
        const uint8_t* ptr = buf + offset;

        // [02] check 0xFFE2
        if (ptr[0] != 0xFF || ptr[1] != 0xE2) {
            logger.rs_error(" [Error] Invalid signature: expected FFE2, got %02X%02X", ptr[0], ptr[1]);
            return false;
        }

        ptr += 4; // 跳过签名和大小

        std::memcpy(rgbTimeStamp, ptr, sizeof(rgbFrameTimeStamp));

        // 可以添加字节序转换
        // rgbTimeStamp->timestampS = ntohs(rgbTimeStamp->timestampS);
        // rgbTimeStamp->timestamp = ntohll(rgbTimeStamp->timestamp);

        return true;
    }

    static inline bool parserIrFrameTimeStamp(const uint8_t* buf, uint64_t* timeStamp) {
        // Initial logger.
        RCLogger& logger = RCLogger::getInstance();
        if (buf == NULL || timeStamp == NULL) {
            logger.rs_error(" [Error] (parserDepthFrameTimeStamp) Please check input ptr! nullptr");

            return false;
        }

        const uint8_t* ptr = buf;

        ptr += 8;

        std::memcpy(timeStamp, ptr, sizeof(uint64_t));

        return true;
    }


    inline bool decodeMjpeg2Rgb(const uint8_t* data, DecodeImage& image, int size) {

        CamLogger& logger = CamLogger::getInstance();
        // CamTimer timer(__func__);

        struct jpeg_decompress_struct cinfo;
        struct jpeg_error_mgr jerr;

        cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_decompress(&cinfo);
        jpeg_mem_src(&cinfo, data, size);

        jpeg_read_header(&cinfo, TRUE);

        jpeg_start_decompress(&cinfo);

    #if 0
        logger.rs_info(" [Info] Tracking cinfo, w: {}, h: {}, c: {}, out w: {}, h: {}, c: {}",
                    cinfo.image_width,
                    cinfo.image_height,
                    cinfo.num_components,
                    cinfo.output_width,
                    cinfo.output_height,
                    cinfo.output_components);
    #endif

        int row_stride = cinfo.output_width * cinfo.output_components;
        int all_size = row_stride * cinfo.output_height;
        if (size != all_size) {
            logger.rs_error(" [Error] Size not match, In: {}, Real: {}", size, all_size);
            return false;
        }

        image.width = cinfo.image_width;
        image.height = cinfo.image_height;
        int num_channels = cinfo.num_components;

        if (!image.data) {
            image.data = std::make_unique<unsigned char[]>(image.width * image.height * num_channels);
        }

        unsigned char* tmp_buffer = image.data.get();

        while (cinfo.output_scanline < cinfo.output_height) {
            jpeg_read_scanlines(&cinfo, &tmp_buffer, 1);
            tmp_buffer += row_stride;
        }

        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);

        return true;
    }
}
